/* Construct a fully qualified filename from 'fname':
 * . and .. in a path are expanded.
 * In DOS, drive designator A: is added.
 * In Unix, ~/ and ~login/ are expanded. Symbolic links are replaced by their
 * 'real' names if HAS_SYMBOLIC_LINKS is defined.
 * Depends on SM_TURBOC, SM_XENIX, SM_SYSV or SM_SUNOS .. define only one to '1'.
 * This file is standalone - should compile separately.
 */

/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org

    Copyright (C) 1999-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/

#include <stdio.h>
#include <string.h>

#ifdef SM_TURBOC
#include <dir.h>
#define MEMCPY(a, b, c)	strcpy((a), (b)) /* can do overlapping copy */
#else
#include <pwd.h>
#include <sys/types.h>
#define MAXPATH 255
/* extern char	*strstr(); */
#undef HAS_SYMBOLIC_LINKS
#endif

#ifdef SM_XENIX
#include <sys/dirent.h>
#define MEMCPY(a, b, c)	strcpy((a), (b)) /* can do overlapping copy */
#undef HAS_SYMBOLIC_LINKS
#endif

#ifdef SM_SUNOS
#include <dirent.h>
#define MEMCPY(a, b, c)	bcopy((b), (a), (c))
#define HAS_SYMBOLIC_LINKS
#endif

#ifdef SM_SYSV
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#define MEMCPY(a, b, c)	strcpy((a), (b))/* This is actually h/w dependant */
#define HAS_SYMBOLIC_LINKS
#endif

#ifdef SM_TURBOC
void fqn(fullname, fnamep)
    char    *fullname,
	    *fnamep;
#else
void fqn(fullname, fname)
    char    *fullname, 
	    *fname;
#endif
{
    /* Possibilities are	<<NULL STRING>>
     *			grot.c
     *			.
     *			..
     *			./
     *			../
     *			./grot.c
     *			../grot.c
     *			/
     *			/a/b/c/grot.c
     *			/a/./../grot.c
     * ... and the same set but with the A: prefix in MSDOS.
     */
    char		*fnp, *d, fbuf[MAXPATH + 1];
#ifdef SM_TURBOC
    char		*fname;
    int			drive,
    			old_drive;
    
    fname = fbuf;
    strcpy(fname, fnamep);
#else
    struct passwd	*pwent, *getpwuid(), *getpwnam();
    int			num_chars;
#endif
    
    fnp = fullname;
    *fnp = 0;
    
#ifdef SM_TURBOC
    while (d = strchr(fname, '\\'))
	*d = '/';
    strlwr(fname);
    
    /* Look for A: prefix; if there remove it */
    drive = -1;
    if (strlen(fname) > 1)
	if (fname[1] == ':') {
	    drive = *fname - 'a';
	    fname += 2;
	}
    
    old_drive = getdisk();
    if (drive < 0)
	drive = old_drive;
    
    /* Because of a bug in TurboC 2.0 'getcurdir' we call must use the Unix 'getcwd'
     * This means that the current drive must be set to the drive for 'fname' before
     * calling 'getcwd'.
     */
    if (*fname != '/') {
	if (drive != old_drive) {
	    setdisk(drive);
	    getcwd(fullname, MAXPATH);
	    setdisk(old_drive);
	} else
	    getcwd(fullname, MAXPATH);
	strcpy(fullname, &fullname[2]);
	while (d = strchr(fullname, '\\'))
	    *d = '/';
	strlwr(fullname);
	fnp = fullname + strlen(fullname);
	
	/* Add trailing / if necessary (Could be '/') */
	if (*(fnp - 1) != '/')
	    strcat(fnp++, "/");
    }
#else /* Not TURBOC */
    if (*fname == '~') {
	fname++;
	if (*fname == '/') {
	    strcpy(fullname, "~"); /* In case getpwuid fails */
	    pwent = getpwuid(getuid());
	    fname++;
	} else {
	    strcpy(fullname, fname);
	    if ((d = strchr(fullname, '/')) != NULL)
		*d = 0;
	    pwent = getpwnam(fullname);
	    if (d)
		fname = strchr(fname, '/') + 1;
	    else
		fname = fname + strlen(fname);
	}
	endpwent();
	if (pwent)
	    strcpy(fullname, pwent->pw_dir);
	fnp = fullname + strlen(fullname);
	if (*(fnp - 1) != '/')
	    strcat(fnp++, "/");
    } else if (*fname != '/') {
	getcwd(fullname, MAXPATH);
	fnp = fullname + strlen(fullname);
	
	/* Add trailing / if necessary (Could be '/') */
	if (*(fnp - 1) != '/')
	    strcat(fnp++, "/");
    }
#endif
    
    /* Now copy the rest of the string */
    fnp = fullname + strlen(fullname);
    strcpy(fnp, fname);
    
    /* Look for // */
    while ((d = strstr(fullname, "//")) != NULL)
	MEMCPY(d, d+1, strlen(d) + 1);
    
    /* Remove trailing / if necessary */
    fnp = fullname + strlen(fullname) - 1;
    if ((*fnp == '/') && (fnp > fullname))
	*fnp = 0;
    
    /* Look for /.. filenames */
    fnp = fullname;
    while (fnp) {
	fnp = strstr(fnp, "/..");
	if (fnp) {
	    if ((*(fnp + 3) == '/') || (*(fnp + 3) == 0)) {
		d = fnp - 1;
		while ((d > fullname) && (*d != '/'))
		    d--;
		if (d >= fullname)
		    d--;
		d++;
		MEMCPY(d, fnp + 3, strlen(fnp + 3) + 1);
		fnp = d;
	    } else
		fnp += 3;
        }
    }
    
    /* Remove /. filenames */
    fnp = fullname;
    while (fnp) {
	fnp = strstr(fnp, "/.");
	if (fnp) {
	    if ((*(fnp + 2) == '/') || (*(fnp + 2) == 0))
		MEMCPY(fnp, fnp + 2, strlen(fnp + 2) + 1);
	    else
		fnp += 2;
        }
    }
    
    /* Make sure there is at least a / */
    if (!*fullname)
	strcpy(fullname, "/");
#ifdef SM_TURBOC
    /* Put in drive A: */
    strcpy(fbuf, fullname);
    fullname[0] = 'a' + drive;
    fullname[1] = ':';
    strcpy(&fullname[2], fbuf);
    
#endif
    
#ifdef HAS_SYMBOLIC_LINKS /* Unix ... handle symbolic links */
    fnp = &fullname[1]; /* Pass over initial '/' */
    if (*fnp) { /* ie not "/" */
	*fbuf = 0;
	while (*fnp) {
	    d = strchr(fnp, '/');
	    if (d)
		*d = 0;
	    strcat(fbuf, "/");
	    strcat(fbuf, fnp);
	    if ((num_chars = readlink(fbuf, fbuf, MAXPATH)) == -1) {
		/* Assume readlink failed because it is not a symlink */
	    } else
		fbuf[num_chars] = 0;
	    
	    if (d)
		fnp = d + 1;
	    else
		fnp = fnp + strlen(fnp);
	}
	strcpy(fullname, fbuf);
    }
#endif
}

#ifdef TEST
main(argc, argv)
    int	argc;
    char	**argv;
{
    char	fullname[MAXPATH], fname[MAXPATH];
    
    do {
	puts("Enter filename");
	gets(fname);
	fqn(fullname, fname);
	printf("%s expanded = \"%s\"\n", fname, fullname);
    } while (1);
}
#endif


/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
